#include "Transform.h"
#include "GameNode.h"
#include <gtc/matrix_transform.hpp>

Transform::Transform(RefPtr<GameNode> pNode) 
	: AComponent(pNode)
{
	mRotation = glm::quat(glm::vec3(0,0,0));
	mWasModified = true;

	mForward = glm::vec3(0,0,1);
	mRight = glm::vec3(1,0,0);
	mUp = glm::vec3(0,0,1);
}

void Transform::setLocalPosition(glm::vec3 pPos)
{
	mLocalPosition = pPos;

	if(mGameNode->getParent().getRef() != NULL)
	{
		glm::vec4 lTemp = mGameNode->getParent()->getTransform()->mPosMatrix * glm::vec4(pPos, 1.0f);
		mPosition = lTemp.swizzle(glm::X, glm::Y, glm::Z)/lTemp.w ;
	}
	else
	{//parent null, the local pos is the global pos
		mPosition = mLocalPosition;
	}

	mPosMatrix = glm::gtc::matrix_transform::translate(glm::mat4(), mPosition);

	mWasModified = true;
}

void Transform::setPosition(glm::vec3 pPos)
{
	mPosition = pPos;
	mPosMatrix = glm::gtc::matrix_transform::translate(glm::mat4(), mPosition);

	if(mGameNode->getParent().getRef() != NULL)
	{
		glm::vec4 lTemp = glm::inverse(mGameNode->getParent()->getTransform()->mPosMatrix) * glm::vec4(pPos, 1.0f);
		mLocalPosition = lTemp.swizzle(glm::X, glm::Y, glm::Z)/lTemp.w ;
	}
	else
	{//parent null, the global pos is the local pos
		mLocalPosition = mPosition;
	}

	mWasModified = true;
}

void Transform::setLocalRotation(glm::quat pRot)
{
	mLocalRotation = pRot;

	if(mGameNode->getParent().getRef() != NULL)
	{
		mRotation = mGameNode->getParent()->getTransform()->mRotation * pRot;
	}
	else
	{//parent null, the local pos is the global pos
		mRotation = mLocalRotation;
	}

	mRotMatrix = glm::gtc::quaternion::mat4_cast(mRotation);

	mWasModified = true;
}

void Transform::setRotation(glm::quat pRot)
{
	mRotation = pRot;
	mRotMatrix = glm::gtc::quaternion::mat4_cast(mRotation);

	if(mGameNode->getParent().getRef() != NULL)
	{
		mLocalRotation = glm::inverse(mGameNode->getParent()->getTransform()->mRotation) * pRot;
	}
	else
	{//parent null, the global pos is the local pos
		mLocalRotation = mLocalRotation;
	}

	mWasModified = true;
}

void Transform::rotateLocalAround(const glm::vec3 &pAxis, const float &pAngle)
{
	setLocalRotation(glm::gtc::quaternion::rotate(mLocalRotation, pAngle, pAxis));
}

void Transform::rotateAround(const glm::vec3 &pAxis, const float &pAngle)
{
	setRotation(glm::rotate(mRotation, pAngle, pAxis));
}

glm::mat4 Transform::getMatrix()
{
	if(mWasModified)
	{
		mWasModified = false;
		mMatrix = mScaleMatrix * mPosMatrix * mRotMatrix;
	}

	return mMatrix;
}

void Transform::Update()
{
	if(mWasModified)
	{
		mWasModified = false;
		mMatrix = mScaleMatrix * mPosMatrix * mRotMatrix;

		glm::vec4 lTemp;
		
		lTemp = mMatrix * glm::vec4(glm::vec3(0,0,1), 1.0);
		mForward = glm::vec3(lTemp.x/lTemp.w, lTemp.y/lTemp.w, lTemp.z/lTemp.w);
		
		lTemp = mMatrix * glm::vec4(glm::vec3(1,0,0), 1.0);
		mRight = glm::vec3(lTemp.x/lTemp.w, lTemp.y/lTemp.w, lTemp.z/lTemp.w);

		lTemp = mMatrix * glm::vec4(glm::vec3(0,1,0), 1.0);
		mUp = glm::vec3(lTemp.x/lTemp.w, lTemp.y/lTemp.w, lTemp.z/lTemp.w);
	}
}

//-----------------------------------------------------------------------------

glm::vec3 Transform::getForward()
{
	if(mWasModified)
	{
		Update();
	}

	return mForward;
}

//-----------------------------------------------------------------------------

glm::vec3 Transform::getRight()
{
	if(mWasModified)
	{
		Update();
	}

	return mRight;
}

//-----------------------------------------------------------------------------

glm::vec3 Transform::getUp()
{
	if(mWasModified)
	{
		Update();
	}

	return mUp;
}